// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

use tauri_react_app_lib::commands::storage::{
    storage_delete_row, storage_execute_sql, storage_insert_row, storage_list_tables,
    storage_read_table, storage_reset_database, storage_update_row, AppDb,
};
use tauri_react_app_lib::commands::password::{
    check_master_password_exists, set_master_password, verify_master_password,
    encrypt_password_data, decrypt_password_data, save_password, get_passwords, delete_password,
    clear_master_password_cache, change_master_password, MasterPasswordCache,
};
use tauri_react_app_lib::commands::model::{
    create_ai_model, get_ai_models, get_ai_model, update_ai_model, delete_ai_model,
    set_default_ai_model, get_default_ai_model,
};
use tauri_react_app_lib::commands::chat::{
    create_conversation, get_conversations, get_conversation, update_conversation,
    update_conversation_model, delete_conversation, get_messages, save_message, send_chat_message,
};
use rusqlite::{Connection, Result};
use std::sync::Mutex;
use tauri::{AppHandle, Manager, State, WebviewWindow};

#[cfg(target_os = "macos")]
use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial};

use tauri_react_app_lib::*;

/// Initialize logger to write to file
fn init_logger() {
    use simplelog::*;
    use std::fs::File;
    
    // Get exe directory
    let exe_path = std::env::current_exe().unwrap_or_else(|_| {
        std::path::PathBuf::from(".")
    });
    
    let exe_dir = exe_path.parent().unwrap_or_else(|| {
        std::path::Path::new(".")
    });
    
    // Create log file path
    let log_file_path = exe_dir.join("app.log");
    
    println!("Log file path: {:?}", log_file_path);
    
    // Try to open or create log file
    match File::create(&log_file_path) {
        Ok(log_file) => {
            // Configure logger with both file and terminal output
            let mut config_builder = ConfigBuilder::new();
            config_builder.set_time_format_rfc3339();
            let _ = config_builder.set_time_offset_to_local();
            let config = config_builder.build();
            
            // In release mode, only log INFO and above; in debug mode, log everything
            #[cfg(debug_assertions)]
            let log_level = LevelFilter::Debug;
            #[cfg(not(debug_assertions))]
            let log_level = LevelFilter::Info;
            
            let _ = CombinedLogger::init(vec![
                WriteLogger::new(log_level, config.clone(), log_file),
                #[cfg(debug_assertions)]
                TermLogger::new(
                    LevelFilter::Debug,
                    config,
                    TerminalMode::Mixed,
                    ColorChoice::Auto
                ),
            ]);
            
            log::info!("=== Application Started ===");
            log::info!("Log file: {:?}", log_file_path);
        }
        Err(e) => {
            eprintln!("Failed to create log file: {}", e);
            // Fallback to terminal only
            let _ = TermLogger::init(
                LevelFilter::Debug,
                Config::default(),
                TerminalMode::Mixed,
                ColorChoice::Auto
            );
        }
    }
}

/// Initialize the SQLite database
fn init_database(app: &AppHandle) -> Result<Connection> {
    // Get exe directory as the default location for database
    let exe_path = std::env::current_exe()
        .expect("Failed to get executable path");
    
    let exe_dir = exe_path.parent()
        .expect("Failed to get executable directory");
    
    // Keep app_data_dir as fallback for settings lookup
    let app_dir = app
        .path()
        .app_data_dir()
        .expect("Failed to get app data directory");
    
    let default_db_path = exe_dir.join("app.db");
    
    log::info!("=== Database Initialization ===");
    log::info!("Executable directory: {:?}", exe_dir);
    log::info!("App data directory: {:?}", app_dir);
    log::info!("Default database path: {:?}", default_db_path);
    
    // First, try to open default database to check if there's a custom path saved
    let db_path = if default_db_path.exists() {
        if let Ok(temp_conn) = Connection::open(&default_db_path) {
            if let Ok(custom_path) = temp_conn.query_row::<String, _, _>(
                "SELECT value FROM app_settings WHERE key = 'database_path'",
                [],
                |row| row.get(0),
            ) {
                log::info!("Found custom database path: {:?}", custom_path);
                std::path::PathBuf::from(custom_path)
            } else {
                default_db_path.clone()
            }
        } else {
            default_db_path.clone()
        }
    } else {
        default_db_path.clone()
    };
    
    log::info!("Final database path: {:?}", db_path);
    log::info!("Database file exists: {}", db_path.exists());
    
    let conn = Connection::open(&db_path)?;
    log::info!("Database connection opened successfully");
    
    // Create app_settings table for storing application settings
    conn.execute(
        "CREATE TABLE IF NOT EXISTS app_settings (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    )?;
    log::info!("Created app_settings table");
    
    // Create passwords table for password management
    conn.execute(
        "CREATE TABLE IF NOT EXISTS passwords (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            username TEXT,
            encrypted_password TEXT NOT NULL,
            website TEXT,
            notes TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    )?;
    log::info!("Created passwords table");
    
    // Create a sample table for demonstration
    conn.execute(
        "CREATE TABLE IF NOT EXISTS notes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            content TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    )?;
    
    // Create ai_models table for AI model management
    conn.execute(
        "CREATE TABLE IF NOT EXISTS ai_models (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            model_id TEXT NOT NULL,
            model_type TEXT NOT NULL,
            api_endpoint TEXT NOT NULL,
            encrypted_api_key TEXT NOT NULL,
            is_default INTEGER NOT NULL DEFAULT 0,
            is_enabled INTEGER NOT NULL DEFAULT 1,
            max_tokens INTEGER,
            temperature REAL,
            description TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    )?;
    log::info!("Created ai_models table");
    
    // Add is_enabled column if it doesn't exist (for existing databases)
    let _ = conn.execute(
        "ALTER TABLE ai_models ADD COLUMN is_enabled INTEGER NOT NULL DEFAULT 1",
        [],
    );
    
    // Create conversations table for chat history
    conn.execute(
        "CREATE TABLE IF NOT EXISTS conversations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            model_id INTEGER NOT NULL,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (model_id) REFERENCES ai_models(id) ON DELETE CASCADE
        )",
        [],
    )?;
    log::info!("Created conversations table");
    
    // Create messages table for chat messages
    conn.execute(
        "CREATE TABLE IF NOT EXISTS messages (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            conversation_id INTEGER NOT NULL,
            role TEXT NOT NULL,
            content TEXT NOT NULL,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE
        )",
        [],
    )?;
    log::info!("Created messages table");
    
    // Check if database file should be hidden
    let should_hide = conn
        .query_row::<String, _, _>(
            "SELECT value FROM app_settings WHERE key = 'hide_database_file'",
            [],
            |row| row.get(0),
        )
        .ok()
        .map(|v| v == "true")
        .unwrap_or(true); // Default to true (hidden)
    
    if should_hide {
        if let Err(e) = hide_file_windows(&db_path) {
            log::error!("Failed to hide database file: {}", e);
        } else {
            log::info!("Database file hidden successfully");
        }
    }
    
    Ok(conn)
}

/// Example command: get app information
#[tauri::command]
async fn get_app_info() -> Result<String, String> {
    Ok(format!(
        "Tauri React App v{}",
        env!("CARGO_PKG_VERSION")
    ))
}

/// Test database connection
#[tauri::command]
async fn test_database(db: State<'_, AppDb>) -> Result<String, String> {
    let conn = db.0.lock().map_err(|e| e.to_string())?;
    
    // Test basic query
    let result: String = conn
        .query_row("SELECT 'Database connection successful' as message", [], |row| {
            row.get(0)
        })
        .map_err(|e| e.to_string())?;
    
    Ok(result)
}

/// Get current database path
#[tauri::command]
async fn get_database_path(db: State<'_, AppDb>) -> Result<String, String> {
    let conn = db.0.lock().map_err(|e| e.to_string())?;
    
    // Try to get custom path from app_settings
    let custom_path: Result<String, rusqlite::Error> = conn.query_row(
        "SELECT value FROM app_settings WHERE key = 'database_path'",
        [],
        |row| row.get(0),
    );
    
    if let Ok(path) = custom_path {
        return Ok(path);
    }
    
    // If no custom path, return default path (exe directory)
    drop(conn);
    let exe_path = std::env::current_exe()
        .map_err(|e| format!("Failed to get executable path: {}", e))?;
    
    let exe_dir = exe_path.parent()
        .ok_or_else(|| "Failed to get executable directory".to_string())?;
    
    let db_path = exe_dir.join("app.db");
    Ok(db_path.to_string_lossy().to_string())
}

/// Get database file hidden status
#[tauri::command]
async fn get_db_hide_status(db: State<'_, AppDb>) -> Result<bool, String> {
    let conn = db.0.lock().map_err(|e| e.to_string())?;
    
    let status: bool = conn
        .query_row(
            "SELECT value FROM app_settings WHERE key = 'hide_database_file'",
            [],
            |row| {
                let value: String = row.get(0)?;
                Ok(value == "true")
            },
        )
        .unwrap_or(true); // Default to true (hidden)
    
    Ok(status)
}

/// Set database file hidden status
#[tauri::command]
async fn set_db_hide_status(
    db: State<'_, AppDb>,
    hide: bool,
) -> Result<(), String> {
    // Save preference to database
    {
        let conn = db.0.lock().map_err(|e| e.to_string())?;
        conn.execute(
            "INSERT OR REPLACE INTO app_settings (key, value, updated_at) VALUES (?1, ?2, datetime('now'))",
            [
                "hide_database_file",
                if hide { "true" } else { "false" },
            ],
        )
        .map_err(|e| format!("Failed to save hide status: {}", e))?;
    }
    
    // Get current database path
    let exe_path = std::env::current_exe()
        .map_err(|e| format!("Failed to get executable path: {}", e))?;
    
    let exe_dir = exe_path.parent()
        .ok_or_else(|| "Failed to get executable directory".to_string())?;
    
    let db_path = {
        let conn = db.0.lock().map_err(|e| e.to_string())?;
        conn.query_row::<String, _, _>(
            "SELECT value FROM app_settings WHERE key = 'database_path'",
            [],
            |row| row.get(0),
        )
        .unwrap_or_else(|_| exe_dir.join("app.db").to_string_lossy().to_string())
    };
    
    let path = std::path::PathBuf::from(db_path);
    
    #[cfg(target_os = "windows")]
    {
        if hide {
            hide_file_windows(&path)
                .map_err(|e| format!("Failed to hide file: {}", e))?;
        } else {
            unhide_file_windows(&path)
                .map_err(|e| format!("Failed to unhide file: {}", e))?;
        }
    }
    
    Ok(())
}

/// Set custom database path and reinitialize
#[tauri::command]
async fn set_database_path(
    db: State<'_, AppDb>,
    new_path: String,
) -> Result<String, String> {
    use std::path::Path;
    
    let path = Path::new(&new_path);
    
    // Validate the path
    if let Some(parent) = path.parent() {
        if !parent.exists() {
            std::fs::create_dir_all(parent)
                .map_err(|e| format!("Failed to create directory: {}", e))?;
        }
    }
    
    // Save the new path to the current (default) database first
    {
        let conn_guard = db.0.lock().map_err(|e| e.to_string())?;
        conn_guard.execute(
            "INSERT OR REPLACE INTO app_settings (key, value, updated_at) VALUES (?1, ?2, datetime('now'))",
            ["database_path", &new_path],
        ).map_err(|e| format!("Failed to save database path to current db: {}", e))?;
    }
    
    // Open new database connection
    let new_conn = Connection::open(path)
        .map_err(|e| format!("Failed to open database: {}", e))?;
    
    // Create tables in new database
    new_conn.execute(
        "CREATE TABLE IF NOT EXISTS app_settings (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    ).map_err(|e| format!("Failed to create app_settings table: {}", e))?;
    
    new_conn.execute(
        "CREATE TABLE IF NOT EXISTS passwords (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            username TEXT,
            encrypted_password TEXT NOT NULL,
            website TEXT,
            notes TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    ).map_err(|e| format!("Failed to create passwords table: {}", e))?;
    
    new_conn.execute(
        "CREATE TABLE IF NOT EXISTS notes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            title TEXT NOT NULL,
            content TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        )",
        [],
    ).map_err(|e| format!("Failed to create notes table: {}", e))?;
    
    // Save the custom path to the new database as well
    new_conn.execute(
        "INSERT OR REPLACE INTO app_settings (key, value, updated_at) VALUES (?1, ?2, datetime('now'))",
        ["database_path", &new_path],
    ).map_err(|e| format!("Failed to save database path to new db: {}", e))?;
    
    // Replace the old connection with the new one
    let mut conn_guard = db.0.lock().map_err(|e| e.to_string())?;
    *conn_guard = new_conn;
    
    // Check if database file should be hidden
    let conn = db.0.lock().map_err(|e| e.to_string())?;
    let should_hide = conn
        .query_row::<String, _, _>(
            "SELECT value FROM app_settings WHERE key = 'hide_database_file'",
            [],
            |row| row.get(0),
        )
        .ok()
        .map(|v| v == "true")
        .unwrap_or(true); // Default to true (hidden)
    drop(conn); // Release lock
    
    if should_hide {
        if let Err(e) = hide_file_windows(path) {
            log::error!("Failed to hide database file: {}", e);
        } else {
            log::info!("Database file hidden successfully after path change");
        }
    }
    
    Ok(new_path)
}

/// Example command: greeting with name
#[tauri::command]
async fn greet(name: String) -> Result<String, String> {
    Ok(format!("Hello, {}! Welcome to Tauri React App.", name))
}

/// Open password generator window
#[tauri::command]
async fn open_password_generator(app: AppHandle) -> Result<(), String> {
    // Check if window already exists
    if let Some(window) = app.get_webview_window("password-generator") {
        window.show().map_err(|e| e.to_string())?;
        window.set_focus().map_err(|e| e.to_string())?;
        return Ok(());
    }

    // Create new window
    let window = WebviewWindow::builder(
        &app,
        "password-generator",
        tauri::WebviewUrl::App("/password-generator".into())
    )
    .title("密码生成器")
    .inner_size(500.0, 600.0)
    .center()
    .resizable(true)
    .decorations(false)
    .transparent(true)
    .shadow(true)
    .always_on_top(false)
    .build()
    .map_err(|e| e.to_string())?;

    // Apply macOS vibrancy effect if on macOS
    #[cfg(target_os = "macos")]
    {
        let _ = apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None);
    }

    window.show().map_err(|e| e.to_string())?;
    Ok(())
}

/// Exit the application
#[tauri::command]
async fn exit_app(app: AppHandle) {
    log::info!("Application exit requested");
    app.exit(0);
}

/// Log message from frontend
#[tauri::command]
async fn log_from_frontend(level: String, message: String) {
    match level.as_str() {
        "error" => log::error!("[FRONTEND] {}", message),
        "warn" => log::warn!("[FRONTEND] {}", message),
        "info" => log::info!("[FRONTEND] {}", message),
        "debug" => log::debug!("[FRONTEND] {}", message),
        _ => log::info!("[FRONTEND] {}", message),
    }
}


fn main() {
    // Initialize logger to write to file in exe directory
    init_logger();

    tauri::Builder::default()
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_fs::init())
        .plugin(tauri_plugin_clipboard_manager::init())
        .setup(|app| {
            log::info!("=== Tauri Setup Starting ===");
            
            // Initialize database
            log::info!("Initializing database...");
            let conn = match init_database(&app.handle()) {
                Ok(c) => {
                    log::info!("Database initialized successfully");
                    c
                }
                Err(e) => {
                    log::error!("Failed to initialize database: {}", e);
                    return Err(Box::new(e));
                }
            };
            
            // Store database connection in app state
            log::info!("Managing AppDb state...");
            app.manage(AppDb(Mutex::new(conn)));
            log::info!("AppDb state managed successfully");
            
            // Initialize master password cache
            log::info!("Managing MasterPasswordCache state...");
            app.manage(MasterPasswordCache(Mutex::new(None)));
            log::info!("MasterPasswordCache state managed successfully");

            // Apply macOS vibrancy effect
            #[cfg(target_os = "macos")]
            {
                let window = app.get_webview_window("main").unwrap();
                let _ = apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None);
            }

            log::info!("=== Tauri Setup Completed ===");
            Ok(())
        })
        .invoke_handler(tauri::generate_handler![
            // Example commands
            get_app_info,
            greet,
            test_database,
            
            // Database path management
            get_database_path,
            set_database_path,
            get_db_hide_status,
            set_db_hide_status,
            
            // Window management commands
            open_password_generator,
            exit_app,
            
            // Logging
            log_from_frontend,
            
            // Storage (Database) commands
            storage_list_tables,
            storage_read_table,
            storage_update_row,
            storage_delete_row,
            storage_insert_row,
            storage_execute_sql,
            storage_reset_database,
            
            // Password management commands
            check_master_password_exists,
            set_master_password,
            verify_master_password,
            encrypt_password_data,
            decrypt_password_data,
            save_password,
            get_passwords,
            delete_password,
            clear_master_password_cache,
            change_master_password,
            
            // AI Model management commands
            create_ai_model,
            get_ai_models,
            get_ai_model,
            update_ai_model,
            delete_ai_model,
            set_default_ai_model,
            get_default_ai_model,
            
            // AI Chat commands
            create_conversation,
            get_conversations,
            get_conversation,
            update_conversation,
            update_conversation_model,
            delete_conversation,
            get_messages,
            save_message,
            send_chat_message,
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
